package com.kuang.lock;

import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
import sun.awt.geom.AreaOp;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

// 测试自旋锁
// 不断尝试获取锁加锁，直到成功为止
// 实现自旋锁：实现方式CAS  比较并交换，达到预期时更新，否则死循环
// 测试自旋锁：线程1、线程2，
//      线程1加锁后，线程2想加锁，得等T1解锁后，T2才能加锁，T2等T1解锁的时候，T2在自旋等待T1释放锁
public class MySpinLock {

    // 加锁对象是一个个线程，所以泛型里面是<Thread>
    AtomicReference<Thread> atomicReference = new AtomicReference();  // 原子引用：避免多线程并发指令重排

    // 本质：控制一次原子引用里面有一个线程
    // 加锁
    public void myLock(){
        // 获取当前线程，如果原子引用里面是Null，就把当前线程放入，如果有线程，进入死循环，一直等待,直到其他线程释放原子引用
        Thread currentThread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "==> mylock");
        // 自旋锁
        while (!atomicReference.compareAndSet(null,currentThread)){
            //System.out.println(Thread.currentThread().getName() + "==> 在自旋等待锁被释放");
            // 返回false，代表没有达到预期更新，就说明原子引用不为null,陷入死循环，相当于加锁
        }
    }

    // 释放锁
    public void myUnLock(){
        // 如果原子引用里面线程是当前线程，就释放原子引用为null,结束死循环，相当于解锁
        Thread currentThread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "==> myUnlock");
        atomicReference.compareAndSet(currentThread,null);
    }
}

// 测试自己写的自旋锁,测试并发
class TestMySpinLock{
    public static void main(String[] args) {
        MySpinLock mySpinLock = new MySpinLock();

        new Thread(()->{
            mySpinLock.myLock();
            try {
                // 等待，业务，通知
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e){
                e.printStackTrace();
            }finally {
                mySpinLock.myUnLock();
            }
        },"A").start();
        new Thread(()->{
            mySpinLock.myLock();
            try {
                // 等待，业务，通知
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e){
                e.printStackTrace();
            }finally {
                mySpinLock.myUnLock();
            }
        },"B").start();
    }
    /*
        A==> mylock
        B==> mylock  A先获取锁，B自旋等待，A释放锁，B获取锁，再释放锁
        A==> myUnlock
        B==> myUnlock

     */
}
